package com.tiantianyuegou.view;

import android.content.Context;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;

import java.util.ArrayList;

public class HeaderGridView extends GridView {
	private static final String TAG = "HeaderGridView";  
    /** 
     * A class that represents a fixed view in a list, for example a header at the top 
     * or a footer at the bottom. 
     */  
    private static class FixedViewInfo {  
        /** The view to add to the grid */  
        public View view;  
        public ViewGroup viewContainer;  
        /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */  
        public Object data;  
        /** <code>true</code> if the fixed view should be selectable in the grid */  
        public boolean isSelectable;  
    }  
    private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();  
    private void initHeaderGridView() {  
        super.setClipChildren(false);  
    }  
    public HeaderGridView(Context context) {  
        super(context);  
        initHeaderGridView();  
    }  
    public HeaderGridView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        initHeaderGridView();  
    }  
    public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
        initHeaderGridView();  
    }  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        ListAdapter adapter = getAdapter();  
        if (adapter != null && adapter instanceof HeaderViewGridAdapter) {  
            ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumns());  
        }  
    }  
    @Override  
    public void setClipChildren(boolean clipChildren) {  
       // Ignore, since the header rows depend on not being clipped  
    }  
    /** 
     * Add a fixed view to appear at the top of the grid. If addHeaderView is 
     * called more than once, the views will appear in the order they were 
     * added. Views added using this call can take focus if they want. 
     * <p> 
     * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap 
     * the supplied cursor with one that will also account for header views. 
     * 
     * @param v The view to add. 
     * @param data Data to associate with this view 
     * @param isSelectable whether the item is selectable 
     */  
    public void addHeaderView(View v, Object data, boolean isSelectable) {  
        ListAdapter adapter = getAdapter();  
        if (adapter != null && ! (adapter instanceof HeaderViewGridAdapter)) {  
            throw new IllegalStateException(  
                    "Cannot add header view to grid -- setAdapter has already been called.");  
        }  
        FixedViewInfo info = new FixedViewInfo();  
        FrameLayout fl = new FullWidthFixedViewLayout(getContext());  
        fl.addView(v);  
        info.view = v;  
        info.viewContainer = fl;  
        info.data = data;  
        info.isSelectable = isSelectable;  
        mHeaderViewInfos.add(info);  
        // in the case of re-adding a header view, or adding one later on,  
        // we need to notify the observer  
        if (adapter != null) {  
            ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();  
        }  
    }  
    /** 
     * Add a fixed view to appear at the top of the grid. If addHeaderView is 
     * called more than once, the views will appear in the order they were 
     * added. Views added using this call can take focus if they want. 
     * <p> 
     * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap 
     * the supplied cursor with one that will also account for header views. 
     * 
     * @param v The view to add. 
     */  
    public void addHeaderView(View v) {  
        addHeaderView(v, null, true);  
    }  
    public int getHeaderViewCount() {  
        return mHeaderViewInfos.size();  
    }  
    /** 
     * Removes a previously-added header view. 
     * 
     * @param v The view to remove 
     * @return true if the view was removed, false if the view was not a header 
     *         view 
     */  
    public boolean removeHeaderView(View v) {  
        if (mHeaderViewInfos.size() > 0) {  
            boolean result = false;  
            ListAdapter adapter = getAdapter();  
            if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {  
                result = true;  
            }  
            removeFixedViewInfo(v, mHeaderViewInfos);  
            return result;  
        }  
        return false;  
    }  
    private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {  
        int len = where.size();  
        for (int i = 0; i < len; ++i) {  
            FixedViewInfo info = where.get(i);  
            if (info.view == v) {  
                where.remove(i);  
                break;  
            }  
        }  
    }  
    @Override  
    public void setAdapter(ListAdapter adapter) {  
        if (mHeaderViewInfos.size() > 0) {  
            HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter);  
            int numColumns = getNumColumns();  
            if (numColumns > 1) {  
                hadapter.setNumColumns(numColumns);  
            }  
            super.setAdapter(hadapter);  
        } else {  
            super.setAdapter(adapter);  
        }  
    }  
    private class FullWidthFixedViewLayout extends FrameLayout {  
        public FullWidthFixedViewLayout(Context context) {  
            super(context);  
        }  
        @Override  
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
			int targetWidth = HeaderGridView.this.getMeasuredWidth() - HeaderGridView.this.getPaddingLeft()
					- HeaderGridView.this.getPaddingRight();
			widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.getMode(widthMeasureSpec));
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        }  
    }  
    /** 
     * ListAdapter used when a HeaderGridView has header views. This ListAdapter 
     * wraps another one and also keeps track of the header views and their 
     * associated data objects. 
     *<p>This is intended as a base class; you will probably not need to 
     * use this class directly in your own code. 
     */  
    private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {  
        // This is used to notify the container of updates relating to number of columns  
        // or headers changing, which changes the number of placeholders needed  
        private final DataSetObservable mDataSetObservable = new DataSetObservable();  
        private final ListAdapter mAdapter;  
        private int mNumColumns = 1;  
        // This ArrayList is assumed to NOT be null.  
        ArrayList<FixedViewInfo> mHeaderViewInfos;  
        boolean mAreAllFixedViewsSelectable;  
        private final boolean mIsFilterable;  
        public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ListAdapter adapter) {  
            mAdapter = adapter;  
            mIsFilterable = adapter instanceof Filterable;  
            if (headerViewInfos == null) {  
                throw new IllegalArgumentException("headerViewInfos cannot be null");  
            }  
            mHeaderViewInfos = headerViewInfos;  
            mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);  
        }  
        public int getHeadersCount() {  
            return mHeaderViewInfos.size();  
        }  
        @Override  
        public boolean isEmpty() {  
            return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0;  
        }  
        public void setNumColumns(int numColumns) {  
            if (numColumns < 1) {  
                throw new IllegalArgumentException("Number of columns must be 1 or more");  
            }  
            if (mNumColumns != numColumns) {  
                mNumColumns = numColumns;  
                notifyDataSetChanged();  
            }  
        }  
        private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {  
            if (infos != null) {  
                for (FixedViewInfo info : infos) {  
                    if (!info.isSelectable) {  
                        return false;  
                    }  
                }  
            }  
            return true;  
        }  
        public boolean removeHeader(View v) {  
            for (int i = 0; i < mHeaderViewInfos.size(); i++) {  
                FixedViewInfo info = mHeaderViewInfos.get(i);  
                if (info.view == v) {  
                    mHeaderViewInfos.remove(i);  
                    mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);  
                    mDataSetObservable.notifyChanged();  
                    return true;  
                }  
            }  
            return false;  
        }  
        @Override  
        public int getCount() {  
            if (mAdapter != null) {  
                return getHeadersCount() * mNumColumns + mAdapter.getCount();  
            } else {  
                return getHeadersCount() * mNumColumns;  
            }  
        }  
        @Override  
        public boolean areAllItemsEnabled() {  
            if (mAdapter != null) {  
                return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();  
            } else {  
                return true;  
            }  
        }  
        @Override  
        public boolean isEnabled(int position) {  
            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)  
            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;  
            if (position < numHeadersAndPlaceholders) {  
                return (position % mNumColumns == 0)  
                        && mHeaderViewInfos.get(position / mNumColumns).isSelectable;  
            }  
            // Adapter  
            final int adjPosition = position - numHeadersAndPlaceholders;  
            int adapterCount = 0;  
            if (mAdapter != null) {  
                adapterCount = mAdapter.getCount();  
                if (adjPosition < adapterCount) {  
                    return mAdapter.isEnabled(adjPosition);  
                }  
            }  
            throw new ArrayIndexOutOfBoundsException(position);  
        }  
        @Override  
        public Object getItem(int position) {  
            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)  
            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;  
            if (position < numHeadersAndPlaceholders) {  
                if (position % mNumColumns == 0) {  
                    return mHeaderViewInfos.get(position / mNumColumns).data;  
                }  
                return null;  
            }  
            // Adapter  
            final int adjPosition = position - numHeadersAndPlaceholders;  
            int adapterCount = 0;  
            if (mAdapter != null) {  
                adapterCount = mAdapter.getCount();  
                if (adjPosition < adapterCount) {  
                    return mAdapter.getItem(adjPosition);  
                }  
            }  
            throw new ArrayIndexOutOfBoundsException(position);  
        }  
        @Override  
        public long getItemId(int position) {  
            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;  
            if (mAdapter != null && position >= numHeadersAndPlaceholders) {  
                int adjPosition = position - numHeadersAndPlaceholders;  
                int adapterCount = mAdapter.getCount();  
                if (adjPosition < adapterCount) {  
                    return mAdapter.getItemId(adjPosition);  
                }  
            }  
            return -1;  
        }  
        @Override  
        public boolean hasStableIds() {  
            if (mAdapter != null) {  
                return mAdapter.hasStableIds();  
            }  
            return false;  
        }  
        @Override  
        public View getView(int position, View convertView, ViewGroup parent) {  
            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)  
            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns ;  
            if (position < numHeadersAndPlaceholders) {  
                View headerViewContainer = mHeaderViewInfos  
                        .get(position / mNumColumns).viewContainer;  
                if (position % mNumColumns == 0) {  
                    return headerViewContainer;  
                } else {  
                    if (convertView == null) {  
                        convertView = new View(parent.getContext());  
                    }  
                    // We need to do this because GridView uses the height of the last item  
                    // in a row to determine the height for the entire row.  
                    convertView.setVisibility(View.INVISIBLE);  
                    convertView.setMinimumHeight(headerViewContainer.getHeight());  
                    return convertView;  
                }  
            }  
            // Adapter  
            final int adjPosition = position - numHeadersAndPlaceholders;  
            int adapterCount = 0;  
            if (mAdapter != null) {  
                adapterCount = mAdapter.getCount();  
                if (adjPosition < adapterCount) {  
                    return mAdapter.getView(adjPosition, convertView, parent);  
                }  
            }  
            throw new ArrayIndexOutOfBoundsException(position);  
        }  
        @Override  
        public int getItemViewType(int position) {  
            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;  
            if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {  
                // Placeholders get the last view type number  
                return mAdapter != null ? mAdapter.getViewTypeCount() : 1;  
            }  
            if (mAdapter != null && position >= numHeadersAndPlaceholders) {  
                int adjPosition = position - numHeadersAndPlaceholders;  
                int adapterCount = mAdapter.getCount();  
                if (adjPosition < adapterCount) {  
                    return mAdapter.getItemViewType(adjPosition);  
                }  
            }  
            return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;  
        }  
        @Override  
        public int getViewTypeCount() {  
            if (mAdapter != null) {  
                return mAdapter.getViewTypeCount() + 1;  
            }  
            return 2;  
        }  
        @Override  
        public void registerDataSetObserver(DataSetObserver observer) {  
            mDataSetObservable.registerObserver(observer);  
            if (mAdapter != null) {  
                mAdapter.registerDataSetObserver(observer);  
            }  
        }  
        @Override  
        public void unregisterDataSetObserver(DataSetObserver observer) {  
            mDataSetObservable.unregisterObserver(observer);  
            if (mAdapter != null) {  
                mAdapter.unregisterDataSetObserver(observer);  
            }  
        }  
        @Override  
        public Filter getFilter() {  
            if (mIsFilterable) {  
                return ((Filterable) mAdapter).getFilter();  
            }  
            return null;  
        }  
        @Override  
        public ListAdapter getWrappedAdapter() {  
            return mAdapter;  
        }  
        public void notifyDataSetChanged() {  
            mDataSetObservable.notifyChanged();  
        }  
    }  
}
